查看原文
其他

持续智能-机器学习项目的11类问题及解决之道

吴志平 凯哥讲故事系列 2022-05-08

    

持续智能:机器学习问题的解决之道

在机器学习这条漫漫长路中,你是否遇到过下列这种情况?

当你刚从一个机器学习网站上学习完理论知识,然后满怀信心,信誓旦旦,准备大刀阔斧,按照教程编写一个狂拽酷炫的算法代码,训练一个机器学习模型,然后觉得自己棒棒哒准备给自己晚饭加个鸡腿感动的要哭的时候,你突然发现自己开发环境Python版本不对,或者这个那个依赖包死活安装不成功,导致一段简单的代码死活跑不起来?

什么?你没有遇到过?好吧,不得不承认现在的机器学习库做的越来越完美了,手动比心。那我们再往下面走一点,假设你现在已经不再是处于机器学习Demo阶段。某一天,你突然有了一个idea,想把最近刚学的机器学习技术应用上去,做一个应用出来,你会不会突然发现,咦,我好像没有足够的数据啊,怎么办?或者模型训练出来了,我怎么把应用服务部署到线上去?我怎么知道我的模型效果好不好?

如果这些问题你都躺过了,那么恭喜你,你已经是一个有经验的机器学习炼丹师了,你可能已经处在调参的路上了,这个时候你可能又会遇到另外一些问题,比如每改一个参数,就要重新训练一波模型,比较结果,时间长了,参数的组合越来越多,你可能已经记不清哪个模型对应的参数是哪些了。这个时候,如果有另外的小伙伴觉得你做的东西挺有意思的,想加入进来,你会发现你趟过的坑他几乎都要再趟一遍。。。

如果上述的问题,你都没有遇到过的话,那就不用浪费时间往下看。如果你有那么一丁点感同身受的话,那么再耐心花几分钟看完全文,或许会对你有一些帮助。

大多数时候,我们以为我们在解决一个机器学习的问题,但实际上我们都是在解决软件工程的开发问题。

简单归纳总结一下,在一个机器学习项目中我们可能会遇到哪些问题(这里主要是针对项目级别,不是个人Demo那种,大多数个人项目不会遇到这么多的问题):

1.机器学习配置问题


任何大型系统都有各种可配置选项,机器学习项目也不例外,包括选择哪些数据,使用哪些特征,用那种算法模型,算法模型给什么样的参数设置,以及潜在预处理或后处理方法,模型验证方法等等。在项目初期的时候,可以继承到项目代码里面,但是随着后面用到的模型越来越复杂,部署的环境越来越多,或者项目越来越多,这时候可能就需要一个统一的配置管理中心统一管理了。

2.数据采集问题


任何机器学习的项目,我们都绕不开数据源问题。如果是离线的批处理的机器学习模型,我们需要考虑数据从什么地方来,怎么来,数据量是什么维度的,数据结构又是怎样的,是直接从数据库里面导过来的结构化信息,还是类似文件、视频、图片这些非结构化的信息。除此之外还要考虑数据怎么存储的问题,根据不同量级别的数据以及数据结构,存储方式不同,是直接存文件系统,还是存关系型数据库或者列式数据库,又或者是key/value缓存数据库。如果是在线实时的机器学习模型,在数据收集的时候就更有讲究了,考虑的问题更多。

3.特征工程问题


解决了数据源的问题,接下来就是如何对数据做处理的问题了。虽说在深度学习领域,已经尽可能的不用数据工程师去处理这些事了,但主要还是针对图片,文本、语音、视频这些非结构化数据。但是对于一些结构化的数据,特别是跟业务强相关的数据,应该怎么来还得怎么来。深度学习不是万能钥匙,解决不了所有问题。

原始的数据中可能包含了太多无效的信息,在输入到机器学习模型之前,要剔除杂质,不然会掐住机器学习的喉咙。我们常说的ETL主要就是在解决特征工程的事情。根据业务逻辑写对应的ETL代码只解决了一部分,而另外一部分问题则是如何把这些ETL任务有效的调度起来,监控起来。在一个任务正常运行过程中,突然挂了要有对应的措施,某个任务的失败可能会影响整个执行链。如果是实时的机器学习项目的话,还要复杂一些,要实时监控流处理任务的状态,看看有没有数据堆积,是否有数据延迟的问题等等。

4.数据预处理问题


你是否以为经过特征工程挖掘之后的数据,就可以直接输入到机器学习模型中,立马开始训练了?远远还不够。虽然在特征工程挖掘过程中已经做了一部分数据预处理的工作了,比如在构建用户画像的时候,根据业务计算出来的指标数据。当这部分内容的侧重点不同,这里的预处理主要指的是数据清洗、集成、变换、以及数据规约相关的操作。

数据清洗很好理解,前面特征工程计算出来的数据中,部分字段可能存在空值,咱们到底应该是删除呢,还是补数据呢?如果是补数据的话怎么个补法呢,到底是用平均值呢,还是用插值呢?如果用插值的话,到底是拉格朗日插值法好呢,还是牛顿插值法好呢(问题就不一一列举了,一般是越挖越细,问题越来越多,当然也越来越清晰)?除了数据丢失以外,还有数据异常问题,怎么识别哪些数据是异常数据,识别出异常数据之后又怎么去处理又是一个问题(可以使用处理缺失值的方式处理)。

那数据集成又是啥呢?我们要处理的数据中可能存在一些实体识别问题,比如同名同义、异名同义,或者单位不统一的问题,需要对他们进行换算调整。比如我们有多份数据集,需要进行Join的时候,可能表示同一个特征的数据在不同数据集中叫法不一样,需要进行冗余识别以及剔除。

数据变换里面的门道就比较多了,最简单的就是做一个函数变换,或者规范化、离散化,如果针对的是时序性的问题的话,可能还需要进行小波变换(我想你应该听说过什么拉普拉斯变换,傅里叶变换吧)。等等规范化是啥,离散化又是啥,怎么这么多要处理的问题,怎么感觉这些问题我都没有遇到过。你没有遇到只能说你所用的那些数据,别人已经帮你准备好了,喂到嘴边了。真实的世界都是要我们自己去从海量的数据中提取出对我们有价值的数据的。这些问题我就不在这里一一细说了,有兴趣的朋友就自行Google吧。

5.数据分析问题


数据预处理之后,我们算是得到一份干净的数据了。讲道理,我们可以开始训练模型了吧。等等,别着急,现在是可以直接训练了,但是到底用什么模型呢?是用分类模型呢,还是回归模型呢?如果用分类模型的话,是用决策树呢,还是用神经网络呢?如果用决策树的话,是用ID3呢还是用随机森林呢?(你有毒吧,你是魔鬼么,怎么这么多问题)

选用什么样的模型还得回到数据上来。当然不可否认,现在有很多AutoML框架,你只需要把整理好的数据放进去就行了,它会帮你选择一个最优的模型。这些工具的确有效而且特别方便,但是带来的另外一个问题就是你不知道为什么它是最优的,就像我们团队同事瀚文在另外一篇文章鹰眼的弓:审计异常检测里面提到的,模型表现的好与坏,业务上是需要知道理由的,也就是说你提供的模型要具有可解释性,而且这样的业务场景还不在少数。

OK,咱们回到选择模型的话题,这个时候你可能要考虑到这些问题,比如这是一个监督学习问题还是非监督学习问题,亦或是半监督学习;这是一个分类问题还是回归问题,或者是聚类问题。确定好这些之后,再看数据的维度和数据量大小。

如果数据量小的话,可以在每个模型跑一遍,看看哪个最优,当然这个方法不是很好,至于为什么,有兴趣的同学可以思考一下。又或者是你可以找到你使用的对应框架的cheat sheet,比如Sklearn的、Azure的,看看你那个场景下选择什么模型最合适(那是无数人呕心沥血,坑摸滚打摸索出来的最佳实践)。

在数据量大的时候,可能你还不能直接跑,一个一个模型的试,因为代价太大。这个时候你可能需要写代码,或者使用一些数据分析工具去分析一下数据的内在联系,比如查看一下数据的分布(这个在分类模型里面特别重要,因为如果每个类别的数据不均匀的话,训练出来的模型会有倾向性),查看数据的相关性、维度,看是否需要先降维,分析数据的Oracle性质(解的稀疏性、无偏性、连续性),看是不是需要考虑变量选择。关于特征选择变量方法的了解,可以参考去年博客大赛的文章机器学习-你用过哪些特征变量选择方法(人工哭泣,排版不好)。当然如果你最后决定自己手写算法模型,比如自己构造一个神经网络模型,你还得考虑惩罚项的问题,到底是用L1 loss引入稀疏解好呢,还是引入L2 loss加快收敛速度好呢?这背后的分析需要你具备一定的理论知识和数学功底。

6.模型训练问题


当你走到这步的时候,算是守的云开见月明了。这个阶段基本上没啥问题,你只需要选择模型,然后调参,关注是否有拟合问题出现。什么,就这么简单?的确就这么简单,主要原因是大部分工作前面都准备好了。当然调参也不是一件容易的活,你得有相关的理论背景知识才行,选择什么样的学习率、优化算法,如果是神经网络的话,网络的层数,每层神经元的个数,甚至图片的大小,batch的大小都是一些受控制而且对结果影响比较大的参数。

除此之外,在调参的过程中,还要有效的管理好每组参数对应的模型,不然如果由于参数组太多,导致管理混乱的话,你搞不清楚哪个模型对应的是哪组参数时,你只有从头再来了。

另外还有一个特别有挑战的问题就是“最后一公里的问题”,假设你的模型评估方式是以准确率来评估的,那么从60%到90%和从90%到91%可能是完全两码事情,一旦业务并不为你90%的准确率买单的话,可能你需要重新设计你的模型了。

7.计算资源管理问题


在模型训练的过程中,在数据量小的情况下,你所有的操作可能都顺风顺水。但是一旦数据量大,或者模型比较复杂耗时长,特别是要占用GPU资源的时候,你的训练过程可能就处处受限制了。一方面计算资源是有限的,也是昂贵的,另外方面可能不只你一个人或者你们一个团队在使用这些资源,其它人也可能在使用。如何管理这些计算资源,如何合理分配,使资源利用率最大又是你不得不考虑的问题。当然这些问题一般可以用机器学习平台解决,前提是你得先有一个机器学习平台。

8.模型训练流程管理问题


这个主要包含两个问题,一个是上面已经提到的,当有多个训练任务的时候,或者任务之间有依赖关系的时候,如何合理的调度任务,保证训练正常运行,同时保证资源的有效利用。另外一个问题,就是训练的结果,一个训练任务可能很耗时,工程师不可能整天蹲在电脑前盯着任务训练结束。所以一方面需要一个工具能够查看历史训练记录,方便炼丹师事后查看以及对比,另一方面要实时监控训练任务,当出现任务异常时及时告警通知,或者重新拉起。这部分内容也可以交给机器学习平台去完成,当然还是那句话,你得先有一个机器学习平台。

9.基础设施服务问题


除了上面提到的计算资源等硬件设施以外,还需要考虑的就是训练以及应用部署时所依赖的软件问题,需要保证每次训练Python的版本,机器学习库的版本,以及一些非超参变量一致,这样才有可对比的价值。很多时候我们不仅仅只看一个模型的“准确率”有多高,还要平衡计算时间等因素。在容器化满地走的今天,我们可以借助Docker镜像和K8s解决这些问题。除此之外,如何快速试错,快速验证结果也是一个问题,不能每次验证都手动操作吧。所以你可能需要一个机器学习持续集成持续部署Pipeline帮你自动化这些工作。

10.监控问题


当我们训练出一个看起来还算不错的模型时,这个时候就应该拉出来接受时代的检验了,部署到相应环境上,向用户直接提供服务了(其实这个流程应该在一开始的时候借助CI/CD就具备了,这样才能快速试错,不断优化,看看我们产出的是不是用户真正想要的)。但是我们怎么知道我们的服务有没有人在用,是不是用户真正所需要的?我们的服务表现的是不是正常?是不是不管接受什么样的请求,都预测的同一个值?所以我们需要知道我们的模型在接收到什么样的请求时返回了什么样的数据。另一方面,如果应用的服务正常的话,那么这些数据可以被添加到训练数据中不断优化模型的性能。

11.潜在的道德伦理问题


如果说上面所有的问题都已经解决了,我们是不是就可以放一百个心了呢?显然不是。如果我们提供的应用支持在线学习,或者整个流程一条龙打通了的话,我们的应用可能会被用户“带坏”,偏离预期的轨道。毕竟它还是一个不成熟的小孩,君不见微软在twitter上的聊天机器人被带偏成了种族歧视者,君又不见一些智能招聘辅助工具出现性别歧视的问题。特别是在社会主义朝阳旗帜下,一些敏感的词汇,或者一些潜在的道德伦理问题要及时监控,及时杜绝。

持续智能:机器学习问题的解决之道


一些老ThoguhtWorker时常告诉我们,作为一个有情怀的ThoughtWroker不要一味的只提问题,还要想想怎么解决。下面咱们就来看看如何解决这些问题。我们简单归纳一下上面列出的可能会遇到的问题,可以简单的归纳成两类:数据工程以及模型训练问题、基础设施服务问题。

数据工程以及模型训练问题因项目而异,和业务绑定,目前并没有一个通用的解决办法,只有一些可以采取的pattern可以参考,这里就不用展开了。至于基础设施服务问题,这是大多数机器学习项目都会遇到的,而且有很多共性,所以机器学习平台能解决大部分问题,我们这里也不一一列举一个成熟完整的机器学习平台是怎么一一去解决上面列到的问题的,毕竟构建一个机器学习平台的成本比较大,不是每个机器学习项目能够承受的。假设没有这样一个平台怎么办?其实从我的角度来看,如果我们能搭建出一套适合机器学习持续集成持续部署的流水线出来,上面的大部分都已经能够被解决了。

在搭建流水线之前,我们先看看环境的问题,针对开发部署环境软件库版本可能不一致的情况,最佳的方式是采用Docker打一个基础镜像,所有开发,训练,以及部署都在同一个镜像上进行。在Docker镜像上开发可能对开发者来说不是特别方便,因为你可能是在容器外进行开发,然后在容器内运行调试,特别是可能只是修改一丁点代码的情况下,这样来回切换也比较麻烦。一个办法就是使用PyCharm专业版这样的IDE,它可以基于Docker容器开发调试解决我们的问题,可惜我没有license体验一下,好气哦。另外还有一种办法就是基于Anaconda、virtualenv这样的虚拟化环境,在多项目的情况下还可以将每个项目所依赖的环境隔离,这样你可以很方便的在本地开发调试了,当然这里面也有一些坑,你可能会发现Mac和Linux在安装某些依赖包的时候表现不一致。当有其它小伙伴想要加进来的时候,只需要提供一个requirements.txt文件,就可以一键准备好相应的环境了,当然这个是一个关于Python项目的通用工程实践问题,并不是机器学习项目特有的,相信小伙伴们有更好的实践。

持续交付里面有句话,越是痛苦的问题,就要越早解决,频繁的解决。大多数项目都用持续集成持续部署的方式自动化的解决我们从开发到上线的一系列问题。当然机器学习的项目也适合,只是有一点点不同而已。比如大多数项目自动触发构建的条件往往是有开发者提交代码,而机器学习项目可能有三部分:一个是项目本身的代码,一个是训练模型的参数(参数和代码可能是分离的),另外一个是数据。这三者任何一个发生变化都会导致构建的结果不一致。

千里之行,始于足下,那我们就来看看一个最精简的机器学习持续集成持续部署流水线Pipeline应该怎么玩,主要流程如下图所示,和通用的Pipeline相比,只是多了一些Stage和数据源,比如训练模型以及验证。这里的数据源主要是指已经处理好可以直接输入到模型里面的数据,在一些数据源不会发生变化的场景下,数据源的触发条件可以去掉。当然也可以把数据工程中的特征工程和数据预处理的步骤加入到Pipeline中。此外,我们还可以把训练任务和服务部署之后用来监控的一些配置在这个Pipeline中添加进入。比如在训练阶段我们可以集成MLflow服务器,将每次训练时选择的模型,以及模型的参数发送到MLflow服务器保存,便于后续比较。服务部署之后,我们可以借助ELK基础设施采集服务的日志信息,分析服务是否正常的运行。


    下面是基于Global持续智能workshop做的一个实践,大家可以参考一下。
    首先,构建了一个简单的预测机器学习模型,和一个预测牛奶价格的应用。


    其次,用GoCD搭建一个CI/CD Pipeline。每次代码提交都会触发一次构建,跑单元测试,训练模型,验证模型。各环节都通过之后,把训练好的模型和应用构建到一个Docker镜像中,然后push到私有仓库。然后在部署环境,直接在对应环境中拉去镜像启动容器,或者完全交给K8S去解决这些问题。


    在每次的训练过程中,把训练的相关参数采集到MLFlow Server中,MLFlow Server会把这些历史数据信息(保存)保存起来,MlFLow Server可以和大部分的存储服务结合,比如分布式文件系统或者像云服务。通过这些历史数据,我们可以比较不同参数下的模型状况。点击MLflow UI可查看详情。


    服务应用部署之后,每当有用户访问服务时,将对应的请求数据和返回结果采集到ElasticSearch中,解决Kibana强大的分析挖掘能力分析服务是否健康的运行。

    点击Kibana UI,time:(from:now%2Fy,mode:quick,to:now))&_a=(columns:!(_source),index:f44ba990-8420-11e9-a02d-c7e625271ca2,interval:auto,query:(language:lucene,query:local.prediction),sort:!(‘@timestamp’,desc),uiState:()))可查看详情。


    以上是关于我们在从零构建一个机器学习项目中可能会遇到的问题,以及一些针对基础服务设施问题的解决方案,至于数据工程和模型训练调优的问题,就不再这里细说了。对上面这个机器学习CI/CD Pipeline感兴趣的朋友,更多内容,可以参考Global的repo和这个repo。

    最后,想再次声明一下,当我们遇到上面问题的时候,我们以为我们是在解决机器学习的问题,其实我们是在解决软件工程的问题。软件工程的问题不是靠一两个数据工程师和算法工程师就能解决的,在这个过程中我们依然需要BA、UX、DevOps等相关的技能,需要多个不同角色的配合才能更加有效率的解决问题。诸子百家,各展其长。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存